import { Pattern, TYPES } from "./EmbroideryPattern";

const getbit = (b, pos) => {
    return (b >> pos) & 1;
};
const bit = (b) => {
    return 1 << b;
};

const decode_dx = (b0, b1, b2) => {
    let x = 0;
    x += getbit(b2, 2) * +81;
    x += getbit(b2, 3) * -81;
    x += getbit(b1, 2) * +27;
    x += getbit(b1, 3) * -27;
    x += getbit(b0, 2) * +9;
    x += getbit(b0, 3) * -9;
    x += getbit(b1, 0) * +3;
    x += getbit(b1, 1) * -3;
    x += getbit(b0, 0) * +1;
    x += getbit(b0, 1) * -1;
    return x;
};

const decode_dy = (b0, b1, b2) => {
    let y = 0;
    y += getbit(b2, 5) * +81;
    y += getbit(b2, 4) * -81;
    y += getbit(b1, 5) * +27;
    y += getbit(b1, 4) * -27;
    y += getbit(b0, 5) * +9;
    y += getbit(b0, 4) * -9;
    y += getbit(b1, 7) * +3;
    y += getbit(b1, 6) * -3;
    y += getbit(b0, 7) * +1;
    y += getbit(b0, 6) * -1;
    return -y;
};

const readStitch = (u8, pattern) => {
    let sequin_mode = false;
    let offset = 0;
    let color_count = 1;

    while (offset < u8.length - 3) {
        const b0 = u8[offset];
        const b1 = u8[offset + 1];
        const b2 = u8[offset + 2];

        const dx = decode_dx(b0, b1, b2);
        const dy = decode_dy(b0, b1, b2);

        if ((b2 & 0b11110011) == 0b11110011) {
            pattern.end(dx, dy);
        } else if ((b2 & 0b11000011) == 0b11000011) {
            color_count += 1;
            pattern.colorChange(dx, dy);
        } else if ((b2 & 0b01000011) == 0b01000011) {
            pattern.sequinMode(dx, dy);
            sequin_mode = !sequin_mode;
        } else if ((b2 & 0b10000011) == 0b10000011) {
            if (sequin_mode) {
                pattern.sequinEject(dx, dy);
            } else {
                pattern.move(dx, dy);
            }
        } else {
            pattern.stitch(dx, dy);
        }

        offset += 3;
    }

    // pattern.end();
    // pattern.invertPatternVertical();
    pattern.setColorsByCount(color_count);
    return pattern;
};

const readHead = (buffer, pattern) => {
    const header = [
        { start: 0, size: 20, key: "LA:", name: "name" }, // ASCII	= "LA:" (label) Name padded with spaces 0x20, sometimes terminated with nul 0x00
        { start: 20, size: 11, key: "ST:", name: "stitch" }, // ASCII	= "ST:" (stitch count) decimal number padded with zero or space
        { start: 31, size: 7, key: "CO:", name: "color" }, // ASCII	= "CO:" (color count) decimal number
        { start: 38, size: 9, key: "+X:", name: "right" }, // ASCII	= "+X:" (maximum X extent) decimal number in tenths of a millimetre
        { start: 47, size: 9, key: "-X:", name: "left" }, // ASCII	= "-X:" (minimum X extent) decimal number in tenths of a millimetre
        { start: 56, size: 9, key: "+Y:", name: "bottom" }, // ASCII	= "+Y:" (maximum Y extent) decimal number in tenths of a millimetre
        { start: 65, size: 9, key: "-Y:", name: "top" }, // ASCII	= "-Y:" (minimum Y extent) decimal number in tenths of a millimetre
        { start: 74, size: 10, key: "AX:", name: "" }, // ASCII	= "AX:" (needle end point X) "+" or "-" decimal number in tenths of a millimetre
        { start: 84, size: 10, key: "AY:", name: "" }, // ASCII	= "AY:" (needle end point Y) "+" or "-" decimal number in tenths of a millimetre
        { start: 94, size: 10, key: "MX:", name: "" }, // ASCII	= "MX:" (previous file needle end point X) "+" or "-" decimal number in tenths of a millimetre
        { start: 104, size: 10, key: "MY:", name: "" }, // ASCII	= "MY:" (previous file needle end point Y) "+" or "-" decimal number in tenths of a millimetre
        { start: 114, size: 10, key: "PD:", name: "" }, // ASCII	= "PD:" (previous file) "******" if single file
        // { start: 124, size: 4, key: "", name: "" }, // ASCII	= 0x1a 0x20 0x20 0x20 or 0x1a 0x00 0x00 0x00 end of header data
        // { start: 128, size: 384, key: "", name: "" }, // ASCII	= spaces 0x20 or sometimes contains colour information
    ];

    const u8 = new Uint8Array(buffer.slice(0, 512));
    const str = u8.reduce((pre, cur) => pre + String.fromCharCode(cur), "");
    const threads = header.map((item) => {
        const name = str.substring(item.start, item.start + 3);
        const val = str.substring(item.start + 3, item.start + item.size);
        const format = val.replace(/(\\r|\s)/g, "");
        return {
            name: name,
            value: format,
        };
    });

    pattern.setThreads(threads);
};

const encodeRecord = (x, y, flags) => {
    y = -y;
    let b0 = 0b00;
    let b1 = 0b00;
    let b2 = 0b00;
    switch (flags) {
        case TYPES.JUMP:
        case TYPES.SEQUIN_EJECT:
            b2 += bit(7); //jumpstitch 10xxxx11
        //bit7 is the difference between move and the stitch encode.
        //fallthrough.
        case TYPES.STITCH:
            b2 += bit(0);
            b2 += bit(1);
            if (x > 40) {
                b2 += bit(2);
                x -= 81;
            }
            if (x < -40) {
                b2 += bit(3);
                x += 81;
            }
            if (x > 13) {
                b1 += bit(2);
                x -= 27;
            }
            if (x < -13) {
                b1 += bit(3);
                x += 27;
            }
            if (x > 4) {
                b0 += bit(2);
                x -= 9;
            }
            if (x < -4) {
                b0 += bit(3);
                x += 9;
            }
            if (x > 1) {
                b1 += bit(0);
                x -= 3;
            }
            if (x < -1) {
                b1 += bit(1);
                x += 3;
            }
            if (x > 0) {
                b0 += bit(0);
                x -= 1;
            }
            if (x < 0) {
                b0 += bit(1);
                x += 1;
            }
            if (x != 0) {
                //ANDROID: Log.e("Error", "Write exceeded possible distance.");
            }
            if (y > 40) {
                b2 += bit(5);
                y -= 81;
            }
            if (y < -40) {
                b2 += bit(4);
                y += 81;
            }
            if (y > 13) {
                b1 += bit(5);
                y -= 27;
            }
            if (y < -13) {
                b1 += bit(4);
                y += 27;
            }
            if (y > 4) {
                b0 += bit(5);
                y -= 9;
            }
            if (y < -4) {
                b0 += bit(4);
                y += 9;
            }
            if (y > 1) {
                b1 += bit(7);
                y -= 3;
            }
            if (y < -1) {
                b1 += bit(6);
                y += 3;
            }
            if (y > 0) {
                b0 += bit(7);
                y -= 1;
            }
            if (y < 0) {
                b0 += bit(6);
                y += 1;
            }
            if (y != 0) {
                //ANDROID: Log.e("Error", "Write exceeded possible distance.");
            }
            break;
        case TYPES.COLOR_CHANGE:
            b2 = 0b11000011;
            break;
        case TYPES.STOP:
            b2 = 0b11110011;
            break;
        case TYPES.END:
            b2 = 0b11110011;
            break;
        case TYPES.SEQUIN_MODE:
            b2 = 0b01000011;
            break;
    }
    return [b0, b1, b2];
};

const getCommands = (stitches) => {
    const prev = { x: 0, y: 0 };
    const commands = [];
    stitches.map((stitch) => {
        let x = stitch[0];
        let y = stitch[1];
        let c = stitch[2];

        let dx = Math.round(x - prev.x);
        let dy = Math.round(y - prev.y);

        prev.x += dx;
        prev.y += dy;
        commands.push(encodeRecord(dx, dy, c));
    });
    return commands;
};

// 读取
export const readDst = (buffer) => {
    const pattern = new Pattern();

    readHead(buffer.slice(0, 512), pattern);
    readStitch(new Uint8Array(buffer.slice(512)), pattern);

    return pattern;
};

// 写入
export const writeDst = (pattern) => {};

// 针迹转base64
export const getBase64ByBuffer = (buffer) => {
    const u8 = new Uint8Array(buffer.slice(512));
    const type = "application/octet-stream";
    // const blob = new Blob([u8], { type: type });
    const blob = new Blob([u8]);
    const reader = new FileReader();

    return new Promise((resolve, reject) => {
        reader.readAsDataURL(blob);
        reader.onloadend = () => resolve(reader.result);
        reader.onerror = (err) => reject(err);
        reader.abort = (err) => reject(err);
    });
};

// 针迹转base64
export const getBase64ByStitch = (stitches) => {
    // const commands = getCommands(stitches);
    // const arr = commands.reduce((p, c) => p.concat(c), []);
    // const u8 = new Uint8Array(arr);
    // const type = "application/octet-stream";
    // // const blob = new Blob([u8], { type: type });
    // const blob = new Blob([u8]);
    // const reader = new FileReader();
    // return new Promise((resolve, reject) => {
    //     reader.readAsDataURL(blob);
    //     reader.onloadend = () => resolve(reader.result);
    //     reader.onerror = (err) => reject(err);
    //     reader.abort = (err) => reject(err);
    // });
};

// base64 转针迹
export const getStitchesByBase64 = (base64) => {
    const blob = window.atob(base64.replace(/^data[^,]+,/, ""));
    const u8 = Uint8Array.from(blob, (v) => v.charCodeAt(0));
    const pattern = new Pattern();

    readStitch(u8, pattern);

    return pattern.stitches;
};
